package com.anc.web.handler;
//import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
//import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.anc.web.annotions.WebController;
import com.anc.web.util.LogUtil;
import com.anc.web.wrapper.WrappedHttpServletRequest;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import enums.BaseRespCodeEnum;
import exception.AncException;
import lombok.extern.slf4j.Slf4j;
import model.Response;
import model.ResponseFactory;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.server.ResponseStatusException;
import util.WebExceptionUtil;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import static constants.CommonConstants.LINE_SEP;


/**
 * @Desciption: web controller异常拦截
 * @author: Anc
 * @date: 2022/7/01
 */
@Slf4j
@ControllerAdvice(annotations = {WebController.class})
public class WebControllerExceptionHandler {

    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public Response handle(HttpServletRequest req, Exception e) {
        try {
            //request wrapper
            req = new WrappedHttpServletRequest(req);
        } catch (Exception exception) {
            log.info("ignore exception:{}", ExceptionUtils.getStackTrace(exception));
            return ResponseFactory.buildErrorResponse(BaseRespCodeEnum.SERVER_ERROR);
        }
        StringBuilder requestInfoBuilder = new StringBuilder();
        // 收集 request uri, header
        LogUtil.collectBaseInfo(req, requestInfoBuilder);
        log.error("{}{}, Occur exception: {}", requestInfoBuilder.toString(), LINE_SEP,
            ExceptionUtils.getStackTrace(e));
        //处理自定义异常
        if (e instanceof AncException) {
            AncException hilifeException = (AncException)e;
            if (BaseRespCodeEnum.REMOTE_THIRD_EXCEPTION.getCode() == hilifeException.getCode()) {
                return ResponseFactory.buildErrorResponse(BaseRespCodeEnum.SERVER_ERROR);
            } else {
                return ResponseFactory.buildErrorResponse(hilifeException.getCode(), hilifeException.getSimpleMsg());
            }
        }
        //处理http status error
        if (e instanceof ResponseStatusException) {
            ResponseStatusException responseStatusException = (ResponseStatusException)e;

            BaseRespCodeEnum respCodeEnum = (BaseRespCodeEnum) WebExceptionUtil.processCodeByHttpStatus(responseStatusException.getStatus());
            return ResponseFactory.buildErrorResponse(respCodeEnum);
        }

//        //处理sentinel限流异常
//        if (e instanceof FlowException || e.getCause() instanceof FlowException) {
//            return ResponseFactory.buildErrorResponse(BaseRespCodeEnum.REQUEST_BLOCKED);
//        }
//        //处理sentinel熔断异常
//        if (e instanceof DegradeException || e.getCause() instanceof DegradeException) {
//            return ResponseFactory.buildErrorResponse(BaseRespCodeEnum.REQUEST_DEGRADE);
//        }
        //处理spring参数校验异常
        if (e instanceof MethodArgumentNotValidException) {
            MethodArgumentNotValidException methodArgumentNotValidException = (MethodArgumentNotValidException)e;
            BindingResult bindingResult = methodArgumentNotValidException.getBindingResult();
            if (null != bindingResult && bindingResult.hasFieldErrors()) {
                List<FieldError> errors = methodArgumentNotValidException.getBindingResult()
                    .getFieldErrors();
                return ResponseFactory.buildErrorResponse(BaseRespCodeEnum.PARAMS_ILLEGAL.getCode(), errors.get(0)
                    .getDefaultMessage());
            }
            return ResponseFactory.buildErrorResponse(BaseRespCodeEnum.PARAMS_ILLEGAL);
        }
        //处理hibernate参数校验异常
        if (e instanceof ConstraintViolationException) {
            ConstraintViolationException exs = (ConstraintViolationException)e;
            Set<ConstraintViolation<?>> violations = exs.getConstraintViolations();
            if (!CollectionUtils.isEmpty(violations)) {
                Iterator<ConstraintViolation<?>> iterator = violations.iterator();
                return ResponseFactory.buildErrorResponse(BaseRespCodeEnum.PARAMS_ILLEGAL.getCode(), iterator.next()
                    .getMessage());
            }
            return ResponseFactory.buildErrorResponse(BaseRespCodeEnum.PARAMS_ILLEGAL);
        }
        //处理参数转换异常
        if (e instanceof HttpMessageNotReadableException) {
            try {
                HttpMessageNotReadableException readableException = (HttpMessageNotReadableException)e;
                Throwable rootCause = readableException.getRootCause();
                if (rootCause instanceof InvalidFormatException) {
                    InvalidFormatException exception = (InvalidFormatException) rootCause;
                    String message = "";
                    List<JsonMappingException.Reference> path = exception.getPath();
                    if (!path.isEmpty()){
                        JsonMappingException.Reference reference = path.get(path.size() - 1);
                        message += "参数名：" + reference.getFieldName() + " 输入不合法，需要的是 " + exception.getTargetType().getName() + " 类型，" + "提交的值是：" + exception.getValue().toString();
                        return ResponseFactory.buildErrorResponse(message);
                    }
                }
            }catch (Exception e1) {
                return ResponseFactory.buildErrorResponse(BaseRespCodeEnum.PARAMS_ILLEGAL);
            }
            return ResponseFactory.buildErrorResponse(BaseRespCodeEnum.PARAMS_ILLEGAL);
        }
        return ResponseFactory.buildErrorResponse(BaseRespCodeEnum.SERVER_ERROR);
    }

}